#include "muduo/proto/codec.h"
#include "muduo/proto/dispatcher.h"
#include "request.pb.h"

#include "muduo/base/Logging.h"
#include "muduo/base/Mutex.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/TcpServer.h"

#include <unistd.h>
#include <functional>

class Server
{
public:
    typedef std::shared_ptr<google::protobuf::Message> MessagePtr;
    typedef std::shared_ptr<Req::TranslateRequest> TranslateRequestPtr;
    typedef std::shared_ptr<Req::AddRequest> AddRequestPtr;


    Server(int port)
    : _server(&_baseloop, muduo::net::InetAddress("0.0.0.0", port), "Server",
      muduo::net::TcpServer::kReusePort),
      // 给 dis 设置一个未知事件处理函数
      _dispatcher(std::bind(&Server::onUnknownMessage, this, std::placeholders::_1,
        std::placeholders::_2, std::placeholders::_3)),
      // codec 复杂对接收到的消息进行 protobuf 处理，得到结构化对象。通过 dispatcher 来执行处理相应事件的函数
      _codec(std::bind(&ProtobufDispatcher::onProtobufMessage, &_dispatcher, 
        std::placeholders::_1, std::placeholders::_2, std::placeholders::_3))
    {
        // 注册事件处理函数
        _dispatcher.registerMessageCallback<Req::TranslateRequest>(std::bind(&Server::onTranslate, 
            this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        _dispatcher.registerMessageCallback<Req::AddRequest>(std::bind(&Server::onAdd, this, 
            std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        
        // 告知服务器：遇到什么事件调用什么函数
        _server.setConnectionCallback(
            std::bind(&Server::onConnection, this, std::placeholders::_1));
        _server.setMessageCallback(std::bind(&ProtobufCodec::onMessage, &_codec, 
            std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
    }

    void start()
    {
        _server.start();
        _baseloop.loop();
    }

private:
    // 未知事件处理函数
    void onUnknownMessage(const muduo::net::TcpConnectionPtr& conn,
                          const MessagePtr& message,
                          muduo::Timestamp)
    {
        LOG_INFO << "onUnknownMessage: " << message->GetTypeName();
        conn->shutdown();
    }


    // 翻译一个单词
    std::string translate(const std::string &word)
    {
        std::unordered_map<std::string, std::string> dict{
            {"hello", "你好"},
            {"world", "世界"},
            {"你好", "hello"},
            {"世界", "world"}
        };

        if (dict.find(word) == dict.end())
            return "查无此单词";
        else
            return dict[word];
    }
    // 处理翻译事件
    void onTranslate(const muduo::net::TcpConnectionPtr& conn,
                    const TranslateRequestPtr& message,
                    muduo::Timestamp)
    {
        // 1.取出 message 中的有效内容
        std::string req_msg = message->msg();
        // 2.翻译
        std::string rsp_msg = translate(req_msg);
        // 3.构建 protobuf 响应
        Req::TranslateResponse rsp;
        rsp.set_msg(rsp_msg);
        // 4.发送响应到客户端
        _codec.send(conn, rsp);
    }

    // 处理加法计算事件
    void onAdd(const muduo::net::TcpConnectionPtr& conn,
                const AddRequestPtr& message,
                muduo::Timestamp)
    {
        // 1.取出 message 中的有效数据
        int num1 = message->num1();
        int num2 = message->num2();
        // 2.计算加法
        int ret = num1 + num2;
        // 3.构建 protobuf 响应
        Req::AddResponse rsp;
        rsp.set_result(ret);
        // 发送
        _codec.send(conn, rsp);
    }

    // 当新连接到来时，连接成功或者断开连接应调用如下函数
    // void onConnection(const muduo::net::TcpConnectionPtr& conn)
    // {
    //     if (conn->connected())
    //         LOG_INFO << "新连接建立成功！";
    //     else
    //         LOG_INFO << "新连接断开！";
    // }

    void onConnection(const muduo::net::TcpConnectionPtr &conn) 
    {
        if (conn->connected())
        {
            LOG_INFO << "新连接建立成功！";
        }
        else
        {
            LOG_INFO << "连接即将关闭！";
        }
    }

private:
    muduo::net::EventLoop _baseloop;
    muduo::net::TcpServer _server;
    ProtobufDispatcher _dispatcher; // 请求分发器，建立 请求-处理函数 的映射关系
    ProtobufCodec _codec;           // 协议处理器，对收到的请求进行 protobuf 协议处理，得到结构化请求对象
};

int main()
{
    Server svr(8085);
    svr.start();
    return 0;
}